import React, { useEffect, useRef } from 'react';
import { mat4 } from 'gl-matrix';
const VIEW_SIZE = 400;
const fragmentShaderCode = `
  precision mediump float;//不写会报错 No precision specified for (float)，缺少精度描述
  varying vec4 v_color;
  void main() {
    gl_FragColor = v_color;//从顶点着色器接收数据
  }`;
const vertexShaerCode = `
  attribute vec4 a_position;
  attribute vec4 a_color;
  varying vec4 v_color;
  uniform mat4 u_ProjViewModelMatrix;//投影矩阵+视图矩阵+模型矩阵
  void main() {
    gl_Position = u_ProjViewModelMatrix*a_position;
    v_color = a_color;//将数据传给片元着色器
  }`;
function getShader(
  gl: WebGLRenderingContext,
  code: string,
  type: 'vertex' | 'fragment'
) {
  // 创建顶点着色器或片段着色器
  let shader: WebGLShader | null = null;
  if (type === 'fragment') {
    shader = gl.createShader(gl.FRAGMENT_SHADER);
  }
  if (type === 'vertex') {
    shader = gl.createShader(gl.VERTEX_SHADER);
  }
  if (!shader) {
    throw new Error('compileShader error');
  }
  // 编译着色器代码
  gl.shaderSource(shader, code);
  gl.compileShader(shader);

  // 检查是否编译成功
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    console.log(gl.getShaderInfoLog(shader)); //打印编译失败信息
    if (!shader) {
      throw new Error('compileShader error');
    }
  }

  return shader;
}
// 初始化着色器程序
function initShaders(gl: WebGLRenderingContext) {
  var fragmentShader = getShader(gl, fragmentShaderCode, 'fragment');
  var vertexShader = getShader(gl, vertexShaerCode, 'vertex');

  // 创建着色器程序并绑定编译好的顶点着色器和片元着色器
  var shaderProgram = gl.createProgram();
  if (!shaderProgram) {
    throw new Error('createProgram error');
  }
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);

  // 链接着色器程序
  gl.linkProgram(shaderProgram);

  // 检查着色器是否成功链接
  if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    throw new Error('linkProgram error');
  }

  // 链接成功后激活渲染器程序
  gl.useProgram(shaderProgram);

  return shaderProgram;
}
// 初始化立方体
function initVertexBuffers(
  gl: WebGLRenderingContext,
  shaderPorgram: WebGLProgram
) {
  // Create a cube
  //    v6----- v5
  //   /|      /|
  //  v1------v0|
  //  | |     | |
  //  | |v7---|-|v4
  //  |/      |/
  //  v2------v3

  //顶点坐标和颜色
  var verticesColors = new Float32Array([
    // Vertex coordinates and color
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    1.0, // v0 White
    -1.0,
    1.0,
    1.0,
    1.0,
    0.0,
    1.0, // v1 Magenta
    -1.0,
    -1.0,
    1.0,
    1.0,
    0.0,
    0.0, // v2 Red
    1.0,
    -1.0,
    1.0,
    1.0,
    1.0,
    0.0, // v3 Yellow
    1.0,
    -1.0,
    -1.0,
    0.0,
    1.0,
    0.0, // v4 Green
    1.0,
    1.0,
    -1.0,
    0.0,
    1.0,
    1.0, // v5 Cyan
    -1.0,
    1.0,
    -1.0,
    0.0,
    0.0,
    1.0, // v6 Blue
    -1.0,
    -1.0,
    -1.0,
    0.0,
    0.0,
    0.0, // v7 Black
  ]);

  // 顶点索引
  var indices = new Uint8Array([
    0,
    1,
    2,
    0,
    2,
    3, // front
    0,
    3,
    4,
    0,
    4,
    5, // right
    0,
    5,
    6,
    0,
    6,
    1, // up
    1,
    6,
    7,
    1,
    7,
    2, // left
    7,
    4,
    3,
    7,
    3,
    2, // down
    4,
    7,
    6,
    4,
    6,
    5, // back
  ]);

  var size = verticesColors.BYTES_PER_ELEMENT; //数组中的每个元素的大小（以字节为单位）
  var num = indices.length;

  //创建数据缓冲区对象
  var vertexBuffer = gl.createBuffer();
  if (!vertexBuffer) {
    console.log('Failed to create the buffer object!');
    return -1;
  }

  //向缓冲区对象中写入数据
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); //将缓冲区对象绑定到目标
  gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

  var a_position = gl.getAttribLocation(shaderPorgram, 'a_position'); //获取attribute变量的存储位置
  //将缓冲区对象分配给a_position变量
  gl.vertexAttribPointer(a_position, 3, gl.FLOAT, false, size * 6, 0);
  //连接a_position变量与分配给它的缓冲区对象
  gl.enableVertexAttribArray(a_position);

  var a_color = gl.getAttribLocation(shaderPorgram, 'a_color');
  gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, size * 6, size * 3);
  gl.enableVertexAttribArray(a_color);

  //创建索引缓冲区对象
  var indexBuffer = gl.createBuffer();
  if (!indexBuffer) {
    console.log('Failed to create the buffer object!');
    return -1;
  }

  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

  //解绑缓冲区对象
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  //gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);//没有绘制完成之前并不能解绑

  return num;
}
export default () => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  useEffect(() => {
    const elCanvas = canvasRef.current;
    if (!elCanvas) {
      return;
    }
    elCanvas.setAttribute('width', VIEW_SIZE + 'px');
    elCanvas.setAttribute('height', VIEW_SIZE + 'px');
    const gl = elCanvas.getContext('webgl');
    if (!gl) {
      return;
    }
    const shaderPorgram = initShaders(gl);
    let rad = 0; // 旋转动画
    const draw = () => {
      const viewMat = mat4.create();
      mat4.lookAt(viewMat, [0, 0, 5], [0, 0, 0], [0, 1, 0]);

      const modelMat = mat4.create();
      rad = (rad + 0.01) % 360;
      mat4.rotate(modelMat, mat4.clone(modelMat), rad, [1, 0, 0]); // x轴旋转
      mat4.rotate(modelMat, mat4.clone(modelMat), rad, [0, 1, 0]); // y轴旋转
      // mat4.rotate(modelMat, mat4.clone(modelMat), rad, [0, 0, 1]); // z轴旋转

      const projMat = mat4.create();
      // mat4.perspective(projMat, 30, VIEW_SIZE / VIEW_SIZE, 1, 100);
      mat4.ortho(projMat, -5, 5, -5, 5, 1, 100);

      // var prjViewModel = projMat.multiply(viewMat).multiply(modelMat);
      const prjViewModel = mat4.create();
      mat4.multiply(prjViewModel, projMat, viewMat);
      mat4.multiply(prjViewModel, mat4.clone(prjViewModel), modelMat);

      gl.clearColor(0.0, 0.0, 1.0, 1.0); //设置背景颜色
      gl.enable(gl.DEPTH_TEST); //开启隐藏面消除
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); //清空颜色和深度缓冲区

      const num = initVertexBuffers(gl, shaderPorgram);

      var u_ProjViewModelMatrix = gl.getUniformLocation(
        shaderPorgram,
        'u_ProjViewModelMatrix'
      );
      gl.uniformMatrix4fv(u_ProjViewModelMatrix, false, prjViewModel.values());
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      gl.drawElements(gl.TRIANGLES, num, gl.UNSIGNED_BYTE, 0);

      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); //绘制完成之后可以解绑索引数据了

      requestAnimationFrame(() => {
        draw();
      });
    };
    draw();
  });
  return (
    <div className='page-cube'>
      <canvas ref={canvasRef} />
    </div>
  );
};
